#include "serial_file.h"
#include "init.h"
#include "common.h"

struct serial_file_cfg {
	struct init_block init;
	struct file *fp;
	const char *file_name;
};

static int init_serial_file(struct init_block *init);
static void cleanup_serial_file(struct init_block *init);

static struct serial_file_cfg cfg = {
	.init = {
		.name = "serial-file",
		.init_fn = init_serial_file,
		.cleanup_fn = cleanup_serial_file,
		.drop_fn = NULL,
	},
	.fp = NULL,
	.file_name = NULL,
};


int init_serial_file(struct init_block *init)
{
	ssize_t ret = 0;
	uint32_t count = 0;
	struct serial_file_cfg *cfg = container_of(init, struct serial_file_cfg, init);

	struct file *f = filp_open(cfg->file_name, O_CREAT | O_RDWR, 0644);
	if (IS_ERR(f)) {
		PR_INFO("open serial file failed, errno: %ld", PTR_ERR(f));
		return PTR_ERR(f);
	}
	cfg->fp = f;
	ret = kernel_read(f, &count, sizeof(uint32_t), &f->f_pos);
	if (ret < 0) {
		PR_ERR("kernel read error, errno: %ld", ret);
		return ret;
	}
	if (ret != sizeof(uint32_t)) {
		count = 0;
		ret = kernel_write(f, &count, sizeof(uint32_t), &f->f_pos);
		if (ret < 0) {
			PR_ERR("kernel write error, errno: %ld", ret);
			return ret;
		}

		if (ret != sizeof(uint32_t)) {
			PR_ERR("write inital serial number to serial file failed");
			return -EINVAL;
		}
	}
	return 0;
}


void cleanup_serial_file(struct init_block *init)
{
	struct serial_file_cfg *cfg = container_of(init, struct serial_file_cfg, init);
	if (cfg->fp != NULL) {
		filp_close(cfg->fp, current->files);
	}
	return;
}


struct file *setup_serial_file(const char *file_name)
{
	int ret = 0;
	cfg.file_name = file_name;

	ret = try_init(&cfg.init);
	if (ret) {
		return ERR_PTR(ret);
	} else {
		return cfg.fp;
	}
}
